/*
 * Copyright 2011 JooKar
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.jookar.gcodecommithook.client;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import nl.jookar.gcodecommithook.model.Commit;
import nl.jookar.gcodecommithook.model.Revision;

import com.google.gdata.client.projecthosting.ProjectHostingService;
import com.google.gdata.data.HtmlTextConstruct;
import com.google.gdata.data.Person;
import com.google.gdata.data.projecthosting.IssueCommentsEntry;
import com.google.gdata.data.projecthosting.SendEmail;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

// Google solution: http://code.google.com/p/support/wiki/IssueTracker#Integration_with_version_control

/**
 * TODO: Write type comment
 *
 * @author Joost A. Bloemsma
 */
public class ProjectWriter {
	private static final Logger LOG = Logger.getLogger(ProjectWriter.class.getName());
	private Pattern issuePattern = Pattern.compile("\\bIssue (\\d+)\\b",Pattern.CASE_INSENSITIVE);
	private static Pattern idPattern = Pattern.compile("(\\d+)");
	private ProjectHostingClient client;
	private static ProjectHostingService service =
        new ProjectHostingService("GCodeCommitHook");

	/**
	 * Constructs a new ProjectWriter instance
	 * @param project
	 * @throws AuthenticationException
	 * @throws MalformedURLException
	 */
	public ProjectWriter(String project) throws AuthenticationException, MalformedURLException {
	    this(project,"gcodecommithook@gmail.com","GCodeHook");
	}

	/**
	 * Constructs a new ProjectWriter instance
	 * @param project
	 * @param username
	 * @param password
	 * @throws AuthenticationException
	 * @throws MalformedURLException
	 */
	public ProjectWriter(String project, String username,
    String password) throws AuthenticationException, MalformedURLException {
		try {
			client = new ProjectHostingClient(service, project, username, password);
		} catch (AuthenticationException e) {
			LOG.severe("AuthenticationException: " + e.getMessage());
			throw e;
		}
	}

	/**
	 * Processes the {@link Commit} object.
	 * All embedded {@link Revision} instances will be added to the project referenced by
	 * the Commit passed.
	 * @param commit
	 * @throws IOException
	 * @throws ServiceException
	 */
	public static void processCommit(Commit commit) throws IOException, ServiceException {
		if (commit == null) {
			return;
		}

	    ProjectWriter writer = new ProjectWriter(commit.getProjectName());

	    for (Revision rev : commit.getRevisions()) {
	        writer.processRevision(rev);
	    }
	}

	/**
	 * Will process the passed {@link Revision} object, thus adding it to the issue
	 * @param rev
	 * @throws IOException
	 * @throws ServiceException
	 */
	public void processRevision(Revision rev) throws IOException, ServiceException {
	    String id = getIssueId(rev);
	    if (id.length() == 0) return;
	    IssueCommentsEntry comment = createCommentsEntry(rev);
	    client.insertComment(id, comment);
	}

	private IssueCommentsEntry createCommentsEntry(Revision rev) {
		IssueCommentsEntry entry = new IssueCommentsEntry();
		Person p = new Person(rev.getAuthor());
	    entry.getAuthors().add(p);
	    String comment = createCommentString(rev);
	    entry.setContent(new HtmlTextConstruct(comment));
	    entry.setSendEmail(new SendEmail("False"));

		return entry;
	}

	/**
	 * Parses the Revision comment. If it finds a pattern that matches the specified
	 * pattern, it extracts the issue ID and returns it.
	 * If no issue ID is found, an empty String ("") is returned.
	 * @param rev
	 * @return The issue ID
	 */
	public String getIssueId(Revision rev) {
		String msg = rev.getMessage();
		String issueId = "";
		Matcher m = issuePattern.matcher(msg);
		if (m.find()) {
		    Matcher idm = idPattern.matcher(m.group());
		    if (idm.find()) {
		        issueId = idm.group();
		    }
		}
		return issueId;
	}

	/**
	 * Generates a comment to add to the issue from a {@link Revision}.
	 * The link is generated automagically by Google Code if it finds
	 * a string like {@code revision #} or {@code r#} in the text.
	 * @param commit
	 * @return
	 */
	private String createCommentString(Revision rev) {
		String sourceUrl = "http://code.google.com/p/" + client.getProject() + "/source/browse%s?r=" + Integer.toString(rev.getRevision());
	    StringBuffer buff = new StringBuffer();
	    buff.append("The following source has been comitted:\n");
	    buff.append("Revision: r" + rev.getRevision() + " \n");
	    buff.append("Author: " + rev.getAuthor() + " \n");
	    buff.append("Timestamp: " + new Date(rev.getTimestamp()).toString() + " \n");
	    buff.append("\n");
	    buff.append("Message:\n");
	    buff.append(rev.getMessage() + " \n");
	    buff.append("\n");
	    // sources are referenced like this:
	    // http://code.google.com/p/<project_name>/source/browse<link from list>?r=<revisionnumber>
	    for (String added : rev.getAdded()) {
	    	buff.append("Added: " + String.format(sourceUrl, added) + "\n");
	    }
	    for (String modified : rev.getModified()) {
            buff.append("Modified: " + String.format(sourceUrl, modified) + "\n");
        }

	    for (String removed : rev.getRemoved()) {
            buff.append("Removed: " + String.format(sourceUrl, removed) + "\n");
        }
		return buff.toString();
	}

}
